package com.smbpics

import com.hierynomus.msdtyp.AccessMask
import com.hierynomus.mssmb2.SMB2CreateDisposition
import com.hierynomus.mssmb2.SMB2ShareAccess
import com.hierynomus.smbj.SMBClient
import com.hierynomus.smbj.SmbConfig
import com.hierynomus.smbj.auth.AuthenticationContext
import com.hierynomus.smbj.connection.Connection
import com.hierynomus.smbj.session.Session
import com.hierynomus.smbj.share.DiskShare
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.ByteArrayOutputStream
import java.util.*
import java.util.concurrent.TimeUnit

data class SmbImageFile(
    val name: String,
    val path: String,
    val size: Long,
    val lastModified: Long,
    val isDirectory: Boolean = false
)

enum class SortType {
    NAME_ASC, NAME_DESC,
    SIZE_ASC, SIZE_DESC,
    DATE_ASC, DATE_DESC,
    CATEGORY_ASC, CATEGORY_DESC
}

class SmbClient {
    private val client: SMBClient
    private var connection: Connection? = null
    private var session: Session? = null
    private var share: DiskShare? = null

    init {
        val config = SmbConfig.builder()
            .withTimeout(60, TimeUnit.SECONDS)
            .withSoTimeout(60, TimeUnit.SECONDS)
            .build()
        client = SMBClient(config)
    }

    suspend fun connect(
        serverAddress: String,
        shareName: String,
        username: String,
        password: String
    ): Boolean = withContext(Dispatchers.IO) {
        try {
            connection = client.connect(serverAddress)
            val authContext = AuthenticationContext(username, password.toCharArray(), null)
            session = connection?.authenticate(authContext)
            share = session?.connectShare(shareName) as? DiskShare
            share != null
        } catch (e: Exception) {
            e.printStackTrace()
            false
        }
    }

    suspend fun listImages(
        directoryPath: String = "",
        sortType: SortType = SortType.CATEGORY_DESC
    ): List<SmbImageFile> = withContext(Dispatchers.IO) {
        val imageExtensions = setOf(
            // Common formats
            "jpg", "jpeg", "png", "gif", "bmp", "webp", "tiff", "tif",
            // RAW formats
            "raw", "arw", "cr2", "cr3", "nef", "orf", "rw2", "pef", "srw", "dng",
            // Adobe formats
            "psd", "psb", "ai", "eps",
            // Vector formats
            "svg", "svgz",
            // High Dynamic Range
            "hdr", "exr",
            // Other formats
            "ico", "cur", "pcx", "tga", "ppm", "pgm", "pbm", "xbm", "xpm",
            // Apple formats
            "heic", "heif", "avif",
            // Medical/Scientific
            "dcm", "dicom",
            // Animation
            "apng", "mng",
            // Print formats
            "pdf", "ps",
            // Fax formats
            "fax", "g3", "g4"
        )
        val files = mutableListOf<SmbImageFile>()

        try {
            share?.let { diskShare ->
                val fileList = diskShare.list(directoryPath)

                for (fileInfo in fileList) {
                    if (fileInfo.fileName == "." || fileInfo.fileName == "..") continue

                    // Filter out hidden files (those starting with ".")
                    if (fileInfo.fileName.startsWith(".")) continue

                    val isImage = imageExtensions.contains(
                        fileInfo.fileName.substringAfterLast('.', "").lowercase()
                    )

                    // Simplified directory detection
                    val isDirectory = fileInfo.fileName.endsWith("/") ||
                                    !fileInfo.fileName.contains(".")

                    if (isImage || isDirectory) {
                        files.add(
                            SmbImageFile(
                                name = fileInfo.fileName,
                                path = if (directoryPath.isEmpty()) fileInfo.fileName
                                       else "$directoryPath/${fileInfo.fileName}",
                                size = 0L, // Simplified - will be populated during actual file access
                                lastModified = System.currentTimeMillis(), // Simplified
                                isDirectory = isDirectory
                            )
                        )
                    }
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }

        sortFiles(files, sortType)
    }

    private fun getFileCategory(file: SmbImageFile): Int {
        return if (file.isDirectory) {
            0 // Directories have highest priority
        } else {
            val extension = file.name.substringAfterLast('.', "").lowercase()
            when (extension) {
                // Common web formats
                "jpg", "jpeg" -> 1
                "png" -> 2
                "gif" -> 3
                "webp" -> 4
                "svg", "svgz" -> 5
                // Modern formats
                "heic", "heif", "avif" -> 6
                "apng", "mng" -> 7
                // Standard formats
                "bmp" -> 8
                "tiff", "tif" -> 9
                "ico", "cur" -> 10
                // RAW formats
                "raw", "arw", "cr2", "cr3", "nef", "orf", "rw2", "pef", "srw", "dng" -> 11
                // Professional formats
                "psd", "psb", "ai", "eps" -> 12
                "hdr", "exr" -> 13
                // Document formats
                "pdf", "ps" -> 14
                // Legacy formats
                "pcx", "tga", "ppm", "pgm", "pbm", "xbm", "xpm" -> 15
                // Medical/Scientific
                "dcm", "dicom" -> 16
                // Fax formats
                "fax", "g3", "g4" -> 17
                else -> 18 // Other files have lowest priority
            }
        }
    }

    private fun sortFiles(files: MutableList<SmbImageFile>, sortType: SortType): List<SmbImageFile> {
        return when (sortType) {
            SortType.NAME_ASC -> files.sortedBy { it.name.lowercase() }
            SortType.NAME_DESC -> files.sortedByDescending { it.name.lowercase() }
            SortType.SIZE_ASC -> files.sortedBy { it.size }
            SortType.SIZE_DESC -> files.sortedByDescending { it.size }
            SortType.DATE_ASC -> files.sortedBy { it.lastModified }
            SortType.DATE_DESC -> files.sortedByDescending { it.lastModified }
            SortType.CATEGORY_ASC -> files.sortedBy { getFileCategory(it) }
            SortType.CATEGORY_DESC -> files.sortedByDescending { getFileCategory(it) }
        }
    }

    suspend fun downloadImage(filePath: String): ByteArray? = withContext(Dispatchers.IO) {
        try {
            share?.let { diskShare ->
                val file = diskShare.openFile(
                    filePath,
                    EnumSet.of(AccessMask.GENERIC_READ),
                    null,
                    SMB2ShareAccess.ALL,
                    SMB2CreateDisposition.FILE_OPEN,
                    null
                )

                val outputStream = ByteArrayOutputStream()
                val buffer = ByteArray(8192)
                var bytesRead: Int
                var offset = 0L

                do {
                    bytesRead = file.read(buffer, offset)
                    if (bytesRead > 0) {
                        outputStream.write(buffer, 0, bytesRead)
                        offset += bytesRead
                    }
                } while (bytesRead > 0)

                file.close()
                outputStream.toByteArray()
            }
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }

    suspend fun searchImages(
        query: String,
        directoryPath: String = "",
        sortType: SortType = SortType.CATEGORY_DESC
    ): List<SmbImageFile> = withContext(Dispatchers.IO) {
        val allImages = listImages(directoryPath, sortType)
        allImages.filter {
            it.name.lowercase().contains(query.lowercase())
        }
    }

    fun disconnect() {
        try {
            share?.close()
            session?.close()
            connection?.close()
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }
}